import time

import openpyxl
from selenium import webdriver
from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait  # 显示等待
from selenium.webdriver.support import expected_conditions as ec  # 等待的条件
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException, ElementNotVisibleException, \
    ElementNotInteractableException

# 创建浏览器对象
driver = webdriver.Chrome()


class TrainSpider():
    login_url = "https://kyfw.12306.cn/otn/resources/login.html"  # 登录页面
    profile_url = "https://kyfw.12306.cn/otn/view/index.html"  # 个人中心页面
    left_ticket = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc"  # 余票查询接口
    confirm_url = "https://kyfw.12306.cn/otn/confirmPassenger/initDc"  # 确认乘车人和坐席

    # 定义init初始化方法
    def __init__(self, from_station, to_station, train_date, trains, passenger_name_lst):
        self.from_station = from_station
        self.to_station = to_station
        self.train_date = train_date
        self.station_code = self.init_statsion_code()
        self.trains = trains
        self.passenger_name_lst = passenger_name_lst
        self.selected_no = None  # 选中的车次
        self.selected_seat = None  # 选中的坐席

    # 登录
    def login(self):
        # 打开登录的页面
        driver.get(self.login_url)
        # 打开后要等待一会，等待扫码登录
        WebDriverWait(driver, 1000).until(
            ec.url_to_be(self.profile_url)  # 直到url页面变成个人中心页面
        )
        print("登录成功")

    # 查询余票
    def search_ticket(self):
        # 打开余票的地址
        driver.get(self.left_ticket)

        # 找到出发站和到达站的隐藏的HTML标签
        from_station_input = driver.find_element(by=By.ID, value="fromStation")
        from_station_text = driver.find_element(by=By.ID, value="fromStationText")

        to_station_input = driver.find_element(by=By.ID, value="toStation")
        to_station_text = driver.find_element(by=By.ID, value="toStationText")

        # 找到出发时间的input标签
        train_date_input = driver.find_element(by=By.ID, value="train_date")

        # 根据车站名称获取车站编码
        from_station_code = self.station_code[self.from_station]  # 根据出发地获取出发地的代号
        to_station_code = self.station_code[self.to_station]  # 根据上目的地获取目的地的代号

        # 执行js,将车站编码及出发日期写到input标签中
        driver.execute_script("arguments[0].value=\"%s\"" % from_station_code, from_station_input)
        driver.execute_script("arguments[0].value=\"%s\"" % self.from_station, from_station_text)

        driver.execute_script("arguments[0].value=\"%s\"" % to_station_code, to_station_input)
        driver.execute_script("arguments[0].value=\"%s\"" % self.to_station, to_station_text)

        driver.execute_script("arguments[0].value=\"%s\"" % self.train_date, train_date_input)

        # 找到查询按钮，并进行单击操作
        query_ticket_tag = driver.find_element(by=By.ID, value="query_ticket")
        query_ticket_tag.click()

        # 解析车次，显示等待，等待tbody的出现
        WebDriverWait(driver, 1000).until(
            ec.presence_of_element_located((By.XPATH, "//tbody[@id=\"queryLeftTable\"]/tr"))
        )
        # 筛选出有数据的tr,去掉属性为datatran的tr
        trains = driver.find_elements(by=By.XPATH, value="//tbody[@id=\"queryLeftTable\"]/tr[not(@datatran)]")

        is_flag = False  # 标记是否有余票，没有余票为false,有余票为true
        # 分别遍历每个车次
        for train in trains:
            # print(train.text)
            # 将回车换行替换成空格，并按空格切分成数组
            infos = train.text.replace("\n", " ").split(" ")
            train_no = infos[0]  # 列表中索引为0的为车次
            if train_no in self.trains:
                # 根据键获取席别，是一个列表
                seat_types = self.trains[train_no]
                for seat_type in seat_types:  # 遍历席位的列表
                    if seat_type == "O":  # 说明是二等座
                        count = infos[9]  # 索引为9的是二等坐
                        if count.isdigit() or count == "有":  # 判断是否有票
                            print("二等坐有余票")
                            is_flag = True
                            break  # 退出遍历席位的循环
                    elif seat_type == "M":  # 说明是一等座
                        count = infos[8]  # 索引为8的是一等坐
                        if count.isdigit() or count == "有":  # 判断是否有票
                            print("一等坐有余票")
                            is_flag = True
                            break  # 退出遍历席位的循环
                # 判断是否有余票
                if is_flag:
                    self.selected_no = train_no  # 记录选中的车次
                    order_btn = train.find_element(by=By.XPATH, value=".//a[@class=\"btn72\"]")
                    order_btn.click()
                    break  # 退出的是遍历车次的循环

    def confirm(self):
        # 等待来到确认乘车人页面
        WebDriverWait(driver, 1000).until(
            ec.url_to_be(self.confirm_url)
        )
        # 等待乘车人列表出来
        WebDriverWait(driver, 1000).until(
            ec.presence_of_element_located((By.XPATH, "//ul[@id=\"normal_passenger_id\"]/li/label"))
        )
        print("开始获取乘车人信息")
        # 获取所有乘车人
        passagers = driver.find_elements(by=By.XPATH, value="//ul[@id=\"normal_passenger_id\"]/li/label")
        for passager in passagers:  # 遍历每个乘车人-label标签
            name = passager.text  # 获取乘车人的姓名
            if name in self.passenger_name_lst:  # 选中乘车人
                passager.click()  # label标签的单击操作

        # 确认席位
        seat_select = Select(driver.find_element(by=By.ID, value="seatType_1"))
        seat_types = self.trains[self.selected_no]  # 获得想要抢票车次的所有席位，self.selected_no是选中车次的键
        for seat_type in seat_types:
            try:
                seat_select.select_by_value(seat_type)  # 选中席位
                self.selected_seat = seat_type  # 记录有票的坐席
            except NoSuchElementException:
                print("没有想要的席位")
                continue
            else:
                break  # 如果有席位，则退出

        # 等待提交订单按钮可点击
        WebDriverWait(driver, 1000).until(
            ec.element_to_be_clickable((By.ID, "submitOrder_id"))
        )
        # 提交订单
        submit_btn = driver.find_element(by=By.ID, value="submitOrder_id")
        submit_btn.click();

        # 等待出现模式对话框出现，核对购票信息
        WebDriverWait(driver, 1000).until(
            ec.presence_of_element_located((By.CLASS_NAME, "dhtmlx_window_active"))
        )
        # 等待确认按钮出来
        WebDriverWait(driver, 5000).until(
            ec.element_to_be_clickable((By.ID, "qr_submit_id"))
        )
        # 点击确认按钮，提交订单
        confirm_btn = driver.find_element(by=By.ID, value="qr_submit_id")
        while confirm_btn:  # 按钮有时候点不到，所以要循环点击，直到找不到这个元素，说明已经提交订单成功
            try:
                confirm_btn.click()
                confirm_btn = driver.find_element(by=By.ID, value="qr_submit_id")
            except ElementNotVisibleException:
                break
            except ElementNotInteractableException:
                break

        print(f'恭喜{self.selected_no}的{self.selected_seat}抢票成功!!!')

    # 封装，只负责调用其他方法，用于组织其他代码
    def run(self):
        # 1.登录
        self.login()
        # 2.余票查询
        self.search_ticket()
        # 3.确认乘车人和订单
        self.confirm()

    # 初始化车站信息
    def init_statsion_code(self):
        wb = openpyxl.load_workbook("车站代码.xlsx")
        wc = wb.active
        lst = []  # 存储车站名称及代码
        for row in wc.rows:  # 遍历所有行
            sub_lst = []  # 用于存储每行中的车站名称及代码
            for cell in row:  # 遍历每行中的单元格
                sub_lst.append(cell.value)
            lst.append(sub_lst)

        return dict(lst)  # 将列表转成字典 并返回给调用处


def start():
    # 车次及席位,O-二等座,M-一等坐
    spider = TrainSpider("深圳北", "铜仁", "2024-05-28", {"G2138": ["O", "M"]}, ["龙文涛"])
    spider.run()
    # spider.init_statsion_code()


if __name__ == '__main__':
    start()
    # time.sleep(5000)
